Skip to content

feat(i18n): serve language packs as versioned, immutable-cacheable scripts#41780

Open
rusackas wants to merge 3 commits into
masterfrom
feat/i18n-versioned-language-pack
Open

feat(i18n): serve language packs as versioned, immutable-cacheable scripts#41780
rusackas wants to merge 3 commits into
masterfrom
feat/i18n-versioned-language-pack

Conversation

@rusackas

@rusackas rusackas commented Jul 5, 2026

Copy link
Copy Markdown
Member

SUMMARY

Follow-up to #39357 (now merged), rebased onto master so the diff is the follow-up only.

#39357 fixes the code-split translation race (#35330) by inlining the Jed language pack into the bootstrap HTML. That's correct but ships ~237KB raw / ~67KB gzipped of pack in every non-English full-page response, since HTML isn't cacheable. This follow-up keeps the synchronous-configuration guarantee while making the pack a first-class cacheable asset:

  • spa.html emits <script src="/language_pack/<lang>/<version>/script.js"> before the entry bundle, where <version> is a short content hash of the pack file (cached in-process alongside the pack itself).
  • The new endpoint serves window.__SUPERSET_LANGUAGE_PACK__ = {…}; with Cache-Control: public, max-age=31536000, immutable — browsers fetch the pack once per translation change instead of once per page load, and cache-bust automatically on upgrade. (The pre-existing unversioned /language_pack/<lang>/ fetch could serve packs up to a year stale after an upgrade, thanks to SEND_FILE_MAX_AGE_DEFAULT.)
  • A stale version in a cached HTML page still gets current content, served no-cache so it can't pin under the wrong address.
  • English emits no tag and pays nothing (unchanged).
  • Packs supplied via COMMON_BOOTSTRAP_OVERRIDES_FUNC still ride the bootstrap payload and suppress the script tag; spa.html stashes them on window instead, so the historical [6.0.0rc1] Partial Translations on pt_BR #35330 workaround keeps working and takes precedence.
  • preamble.ts's fallback chain is unchanged (bootstrap pack → window global → async fetch), so a failed script load degrades gracefully.
  • The endpoint is deliberately unauthenticated: translation catalogs are static public content shipped in this repo, contain no user or tenant data, and must load for anonymous principals (login page, embedded dashboards — note the sibling /language_pack/<lang>/ fetch endpoint's @has_access would 302 for embedded anonymous users, which the script tag avoids).
  • Embedded gets the same treatment: EmbeddedView passes the same template context.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — no visual changes. Behavior change is in caching headers and HTML size.

TESTING INSTRUCTIONS

pytest tests/unit_tests/translations/utils_test.py \
  tests/unit_tests/views/test_language_pack_script.py \
  tests/unit_tests/views/test_bootstrap_auth.py \
  tests/unit_tests/views/test_base.py
cd superset-frontend && npx jest src/preamble.test.ts

Manual: set a non-English locale, load any page, and confirm (1) the <script src="/language_pack/…/script.js"> tag precedes the entry bundle, (2) the response carries immutable, (3) a hard reload serves it from browser cache, (4) translations render in code-split chunks on first paint.

ADDITIONAL INFORMATION

🤖 Generated with Claude Code

@github-actions github-actions Bot added i18n Namespace | Anything related to localization api Related to the REST API packages labels Jul 5, 2026
@netlify

netlify Bot commented Jul 5, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 9f6de65
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a4b255c18db6d00085d6c5b
😎 Deploy Preview https://deploy-preview-41780--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 48.93617% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.70%. Comparing base (8382683) to head (dc475a1).

Files with missing lines Patch % Lines
superset/views/core.py 16.66% 15 Missing ⚠️
superset/translations/utils.py 61.11% 5 Missing and 2 partials ⚠️
superset/views/base.py 80.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #41780      +/-   ##
==========================================
- Coverage   64.72%   64.70%   -0.02%     
==========================================
  Files        2686     2686              
  Lines      148651   148688      +37     
  Branches    34309    34315       +6     
==========================================
- Hits        96213    96211       -2     
- Misses      50676    50709      +33     
- Partials     1762     1768       +6     
Flag Coverage Δ
hive 39.18% <27.65%> (-0.01%) ⬇️
javascript 69.57% <ø> (+<0.01%) ⬆️
mysql 57.82% <48.93%> (-0.03%) ⬇️
postgres 57.88% <48.93%> (-0.04%) ⬇️
presto 40.72% <27.65%> (-0.02%) ⬇️
python 59.28% <48.93%> (-0.04%) ⬇️
sqlite 57.46% <48.93%> (-0.03%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rusackas

rusackas commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

CI status: everything is green now except sharded-jest-tests (5), which is master-side breakage... #41742 renamed the filter delete icon's accessible name and two FiltersConfigModal tests still query the old one, so shard 5 fails on every PR right now. Fix is up in #41781 (two lines). Once that merges I'll update this branch and CI should go fully green.

rusackas and others added 3 commits July 5, 2026 21:16
…ripts

Follow-up to #39357, which inlined the Jed language pack into the
bootstrap HTML to fix the code-split translation race (#35330). That
made non-English full-page loads carry ~67KB (gzipped) of pack on every
request since HTML is uncacheable.

This keeps the synchronous-configuration guarantee but moves the pack
to a content-addressed script: spa.html emits
<script src="/language_pack/<lang>/<version>/script.js"> before the
entry bundle, where <version> is a short content hash of the pack file.
The endpoint serves window.__SUPERSET_LANGUAGE_PACK__ = {...} with
Cache-Control: public, max-age=31536000, immutable, so browsers fetch
the pack once per translation change instead of once per page load, and
cache-bust automatically on upgrade (fixing the stale-pack window the
unversioned /language_pack/<lang>/ fetch had under the 1-year
SEND_FILE_MAX_AGE_DEFAULT).

Details:
- English emits no tag and pays nothing.
- A stale version in a cached HTML page still gets current content,
  served no-cache so it can't pin under the wrong address.
- Packs provided via COMMON_BOOTSTRAP_OVERRIDES_FUNC still ride the
  bootstrap payload and suppress the script tag (spa.html stashes them
  on window instead), preserving the historical workaround.
- The endpoint is deliberately unauthenticated: catalogs are static
  public repo content with no user data, and must load for anonymous
  principals (login page, embedded).
- preamble.ts fallback chain unchanged: bootstrap pack, window global,
  then async fetch if the script failed to load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…antics

Fixes the two CI failures (generator fixture annotated Iterator[None],
stray unused import in the test package init) and adds tests encoding
the delivery contract:

- English configures the translator bare and never loads a pack (zero
  payload, no fetch).
- Non-English configures synchronously from the window global set by
  the versioned classic script tag, with no fetch and no English flash.
- An operator-supplied bootstrap pack takes precedence over the window
  global.
- The fallback fetch requests only the selected locale and resolves
  before initPreamble() returns, so even that path renders translated.
- The endpoint resolves exactly the locale in the URL and nothing else.
- Static guard: spa.html's pack script tag precedes the entry bundle
  and carries no async/defer, preserving execution order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…list the script route

Fixes the three real CI failures:

- unit-tests: get_spa_template_context called common_bootstrap_payload()
  a second time just to derive the language pack context, which requires
  user/auth request state that tests mocking get_spa_payload don't set
  up. get_language_pack_template_context now takes the already-built
  common payload dict, so template context and payload can't diverge
  and the theme-helper tests pass unchanged.
- test-sqlite/mysql/postgres: register the deliberately-unauthenticated
  language_pack_script route in test_views_are_secured's allowlist with
  its justification (static public catalog data, must load for
  anonymous principals).
- lint-frontend: type the preamble test fixture as LanguagePack
  (plural_forms was missing) via a type-only import that the jest
  module mock doesn't affect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rusackas rusackas marked this pull request as ready for review July 6, 2026 04:18
@dosubot dosubot Bot added change:backend Requires changing the backend infra:caching Infra setup and configuration related to caching labels Jul 6, 2026
@rusackas rusackas force-pushed the feat/i18n-versioned-language-pack branch from 9f6de65 to dc475a1 Compare July 6, 2026 04:18
Comment thread tests/unit_tests/translations/utils_test.py
Comment thread tests/unit_tests/translations/utils_test.py
Comment thread tests/unit_tests/translations/utils_test.py
Comment thread tests/unit_tests/views/test_language_pack_script.py
Comment thread tests/unit_tests/views/test_language_pack_script.py
Comment thread superset/translations/utils.py
Comment thread superset/translations/utils.py
Comment thread superset/views/core.py
Comment thread superset/views/core.py
Comment thread superset/views/core.py
Comment thread superset/views/base.py
@bito-code-review

bito-code-review Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #1af492

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/views/core.py - 1
    • Duplicated validation logic · Line 934-936
      Lines 934–936 duplicate the regex validation at lines 908–909 exactly. Extract to a shared `_validate_language_code` helper to reduce divergence risk and keep validation logic single-sourced.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/views/test_language_pack_script.py - 1
Review Details
  • Files reviewed - 12 · Commit Range: 504c3df..dc475a1
    • superset-frontend/src/preamble.test.ts
    • superset-frontend/src/preamble.ts
    • superset/embedded/view.py
    • superset/templates/superset/spa.html
    • superset/translations/utils.py
    • superset/views/base.py
    • superset/views/core.py
    • tests/integration_tests/security_tests.py
    • tests/unit_tests/translations/__init__.py
    • tests/unit_tests/translations/utils_test.py
    • tests/unit_tests/views/test_bootstrap_auth.py
    • tests/unit_tests/views/test_language_pack_script.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Related to the REST API change:backend Requires changing the backend i18n Namespace | Anything related to localization infra:caching Infra setup and configuration related to caching preset-io size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant